package com.atguigu.tingshu.account.service.impl;

import com.atguigu.tingshu.account.mapper.RechargeInfoMapper;
import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.account.RechargeInfo;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private UserAccountDetailMapper userAccountDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RechargeInfoMapper rechargeInfoMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public int checkAndDeduct(AccountDeductVo accountDeductVo) {
        //  调用mapper 层方法.
        int count = userAccountMapper.checkAndDeduct(accountDeductVo.getUserId(),accountDeductVo.getAmount());
        //  判断
        if (count == 0 ){
            throw new GuiguException(400, "账户余额不足！");
        }
        //  记录明细
        this.addUserAccountDetail(
                accountDeductVo.getUserId(),
                accountDeductVo.getContent(),
                SystemConstant.ACCOUNT_TRADE_TYPE_MINUS,
                accountDeductVo.getAmount(),
                accountDeductVo.getOrderNo()
                );
        //  返回受影响的行数
        return count;
    }

    @Override
    public IPage<UserAccountDetail> findUserConsumePage(Page<UserAccountDetail> detailPage, Long userId) {
        //  select * from user_account_detail where user_id = 41 and trade_type = '1204';
        LambdaQueryWrapper<UserAccountDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAccountDetail::getUserId,userId);
        wrapper.eq(UserAccountDetail::getTradeType,"1204");
        return userAccountDetailMapper.selectPage(detailPage,wrapper);
    }

    @Override
    public IPage<UserAccountDetail> findUserRechargePage(Page<UserAccountDetail> detailPage, Long userId) {
        //  select * from user_account_detail where user_id = 41 and trade_type = '1201';
        LambdaQueryWrapper<UserAccountDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAccountDetail::getUserId,userId);
        wrapper.eq(UserAccountDetail::getTradeType,"1201");
        return userAccountDetailMapper.selectPage(detailPage,wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rechargePaySuccess(String orderNo) {
        //  1.  修改充值状态.
        //  select * from recharge_info where order_no = orderNo;
        LambdaQueryWrapper<RechargeInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RechargeInfo::getOrderNo, orderNo);
        RechargeInfo rechargeInfo = this.rechargeInfoMapper.selectOne(wrapper);
        //  判断对象是否为空。或已充值成功
        if (null == rechargeInfo || rechargeInfo.getRechargeStatus().equals(SystemConstant.ORDER_STATUS_PAID)){
            return;
        }
        //  赋值成功：
        rechargeInfo.setRechargeStatus(SystemConstant.ORDER_STATUS_PAID);
        //  更新数据
        rechargeInfoMapper.updateById(rechargeInfo);
        //  2.  给用户账户余额加钱..
        //  update user_account set total_amount = total_amount + ? , available_amount = available_amount + ? ,   total_income_amount = total_income_amount + ? where user_id = ?;
        int count = userAccountMapper.add(rechargeInfo.getUserId(), rechargeInfo.getRechargeAmount());
        if (count == 0) {
            throw new GuiguException(ResultCodeEnum.RECHARGE_FAIL);
        }
        //  3.  记录账户明细. user_account_detail insert
        this.addUserAccountDetail(rechargeInfo.getUserId(), "充值记录", SystemConstant.ACCOUNT_TRADE_TYPE_DEPOSIT, rechargeInfo.getRechargeAmount(), orderNo);

    }

    @Override
    public void unlock(String orderNo) {
        //  update user_account set lock_amount = lock_amount - 100 , available_amount = available_amount + 100 where user_id = 41;
        //	扣减金额：setnx key value;
        String key = "unlock:" + orderNo;
        //	数据key
        String dataKey = "account:lock:" + orderNo;
        Boolean exist = this.redisTemplate.opsForValue().setIfAbsent(key, orderNo, 10, TimeUnit.MINUTES);
        if (!exist) {
            return;
        }
        //  从缓存中获取扣减金额数据
        AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
        //  调用mapper 层方法.
        if (null == accountLockResultVo) {
            //  删除key
            this.redisTemplate.delete(key);
            return;
        }
        //   update user_account set lock_amount = lock_amount - 100 ,total_amount = total_amount - 100 ,total_pay_amount = total_pay_amount + ? where user_id = 41;
        int unlcok = userAccountMapper.unlock(accountLockResultVo.getUserId(), accountLockResultVo.getAmount());
        if (unlcok == 0) {
            //  删除key
            this.redisTemplate.delete(key);
            return;
        }
        //  记录用户流水
        this.addUserAccountDetail(accountLockResultVo.getUserId(), "解锁" + accountLockResultVo.getContent(), SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK, accountLockResultVo.getAmount(), orderNo);
        //  删除锁定信息数据
        this.redisTemplate.delete(dataKey);
    }

    @Override
    public void minus(String orderNo) {
        //	扣减金额：setnx key value;
        String key = "minus:" + orderNo;
        //	数据key
        String dataKey = "account:lock:" + orderNo;
        //  判断是否已经扣减.
        Boolean exist = this.redisTemplate.opsForValue().setIfAbsent(key, orderNo, 10, TimeUnit.MINUTES);
        if (!exist) {
            return;
        }
        //  扣减金额.
        //  从缓存中获取扣减金额数据
        AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
        //  调用mapper 层方法.
        if (null == accountLockResultVo) {
            //  删除key
            this.redisTemplate.delete(key);
            return;
        }
        //   update user_account set lock_amount = lock_amount - 100 ,total_amount = total_amount - 100 ,total_pay_amount = total_pay_amount + ? where user_id = 41;
        int minus = userAccountMapper.minus(accountLockResultVo.getUserId(), accountLockResultVo.getAmount());
        if (minus == 0) {
            //  删除key
            this.redisTemplate.delete(key);
            return;
        }
        //  记录用户流水
        this.addUserAccountDetail(accountLockResultVo.getUserId(), "扣减" + accountLockResultVo.getContent(), SystemConstant.ACCOUNT_TRADE_TYPE_MINUS, accountLockResultVo.getAmount(), orderNo);
        //  删除锁定信息数据
        this.redisTemplate.delete(dataKey);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountLockResultVo checkAndLock(AccountLockVo accountLockVo) {
        //	检查与锁定本质：setnx;
        String key = "checkAndLock:" + accountLockVo.getOrderNo();
        //	数据key
        String dataKey = "account:lock:" + accountLockVo.getOrderNo();
        //	执行 setnx key value;
        Boolean exist = this.redisTemplate.opsForValue().setIfAbsent(key, accountLockVo.getOrderNo(), 10, TimeUnit.MINUTES);
        //	判断key 是否存在
        if (!exist) {
            //	从缓存中获取数据,并返回.
            AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
            //	返回
            return accountLockResultVo;
        }
        //	表示没有人执行过.
        //	select * from user_account where user_id = 41 and available_amount >= 1800 for update;
        //	Connection PrepareStatement{# $Statement} Result
        UserAccount userAccount = userAccountMapper.check(accountLockVo.getUserId(), accountLockVo.getAmount());
        if (null == userAccount) {
            //	删除key
            this.redisTemplate.delete(key);
            return null;
        }
        //	锁定金额
        //	update user_account set lock_amount = lock_amount + 100 , available_amount = available_amount - 100 where user_id = 41;
        int lock = userAccountMapper.lock(accountLockVo.getUserId(), accountLockVo.getAmount());
        if (0 == lock) {
            //	删除key
            this.redisTemplate.delete(key);
            return null;
        }

        //  记录账户明细-user_account_detail，将accountLockResultVo 保存到缓存。
        //	accountLockResultVo 存储到缓存。 真正减少金额的时候，从缓存获取.
        AccountLockResultVo accountLockResultVo = new AccountLockResultVo();
        accountLockResultVo.setUserId(accountLockVo.getUserId());
        accountLockResultVo.setAmount(accountLockVo.getAmount());
        accountLockResultVo.setContent(accountLockVo.getContent());
        this.redisTemplate.opsForValue().set(dataKey, accountLockResultVo, 10, TimeUnit.MINUTES);
        //  记录账户明细-user_account_detail
        this.addUserAccountDetail(accountLockVo.getUserId(), "锁定金额：" + accountLockVo.getContent(), SystemConstant.ACCOUNT_TRADE_TYPE_LOCK, accountLockVo.getAmount(), accountLockVo.getOrderNo());
        //  返回数据
        return accountLockResultVo;
    }

    /**
     * 记录用户流水
     *
     * @param userId
     * @param title
     * @param typeLock
     * @param amount
     * @param orderNo
     */
    private void addUserAccountDetail(Long userId, String title, String typeLock, BigDecimal amount, String orderNo) {
        UserAccountDetail userAccountDetail = new UserAccountDetail();
        userAccountDetail.setUserId(userId);
        userAccountDetail.setTitle(title);
        userAccountDetail.setTradeType(typeLock);
        userAccountDetail.setAmount(amount);
        userAccountDetail.setOrderNo(orderNo);
        userAccountDetailMapper.insert(userAccountDetail);
    }


    @Override
    public BigDecimal getAvailableAmount(Long userId) {
        //	获取可用余额
        return userAccountMapper.selectOne(new LambdaQueryWrapper<UserAccount>().eq(UserAccount::getUserId, userId)).getAvailableAmount();
    }

    @Override
    public void initUserAccount(long userId) {
        //	创建对象
        UserAccount userAccount = new UserAccount();
        userAccount.setUserId(userId);
        userAccount.setTotalAmount(new BigDecimal("1000"));
        userAccount.setAvailableAmount(new BigDecimal("1000"));
        userAccountMapper.insert(userAccount);
    }
}
